module cl {
	export class Judge {

		private static judgeParam : JudgeParam;

		public static init(judgeparam : JudgeParam) : void {
			Judge.judgeParam = judgeparam;
			Judge.refreshPayMapCache();
		}

		public static refreshPayMapCache() : void {
			Judge.payMapCache = [];
			for (let tid = 0; tid < Map.tileEngine.tileCount; tid++) {
				let r = Map.routeEngine.payMap(Campaign.zone.pass_index, tid, 50000).payMap;
				Judge.payMapCache.push(r);
			}
		}

		private static dataSaved : CampaignDataEx;

		private static strengthsAll : number[];

		private static targetTilesAll : Tile[];

		private static strategiesAll : number[];

		private static actions : ActionPerformer[];

		private static payMapCache : number[][]; // tid -> commonPayMap

		public static judge() : ActionPerformer[] {

			// 拷贝 campaignData 作存档
			Judge.dataSaved = Campaign.data;
			Campaign.init(Campaign.data.toPrimitive());

			Judge.actions = [];

			// 发放本回合移动力
			Judge.troopMoveRemainAll();

			// 执行部队重组
			for (let troopid = 0;  troopid < Interaction.direction.regroups.length; troopid++){
				let tid = Interaction.direction.regroups[troopid];
				Judge.tryRegroup(troopid, tid);
			}

			// 刷新 部队策略 缓存
			Judge.cacheStrategiesAll();

			// 创建 部队血量 缓存，用于击溃判定
			Judge.cacheStrengthsAll();

			// 创建 部队目标点位 缓存
			Judge.cacheTargetTilesAll();

			// 部队按移动力大小排序 (仅序号)
			let sortedTroopid = new SortedArray((a , b) : boolean => {
				return Judge.actionPrior(a as number) > Judge.actionPrior(b as number);
			}, this);
			for (let troopid = 0; troopid < Campaign.troop.length; troopid++){
				sortedTroopid.insert(troopid);
			}

			// 开始普通命令
			let oldActionLength = 0;
			do {
				oldActionLength = Judge.actions.length;

				for (let troopid of sortedTroopid.data) {
					let troop = Campaign.troop[troopid];
					if (troop.move_remain <= 0) { // 排序已经到 <= 0 了，后面的也都没有行动力了
						break;
					}

					if (troop.plane == 0) {
						switch (Judge.strategiesAll[troopid]) {
							case 0: // 目标策略
								Judge.targetStrategy(troop, troopid);
								break;
							case 1: // 战线策略
								Judge.lineStrategy(troop, troopid);
								break;
							case 2: // 磁性策略
								Judge.magnetStrategy(troop, troopid);
								break;
						}

					} else if (troop.plane == 1) { // 地面部队移动攻击
						// 间谍等搜集情报

						// 航空兵-导弹轰炸
						if (troop.type_name == "航空兵"){
							Judge.bombStrategy(troop, troopid);
						}

						// 伞兵空降
					}

					// let debugMoveRemain = [];
					// let debugAttackRange = [];
					// let debugType = [];
					// for (let troopid_tmp of sortedTroopid.data) {
					// 	debugMoveRemain.push(Campaign.troop[troopid_tmp].move_remain / Campaign.troop[troopid_tmp].move);
					// 	debugAttackRange.push(Campaign.troop[troopid_tmp].attack_range);
					// 	debugType.push(Campaign.troop[troopid_tmp].type_name);
					// }
					// console.log(sortedTroopid.data);
					// console.log(debugMoveRemain);
					// console.log(debugAttackRange);
					// console.log(debugType);

					// 每生成一个action就重新计算
					if (Judge.actions.length > oldActionLength) {
						for (let i = oldActionLength; i < Judge.actions.length; i++) {
							let troopids = Judge.actions[i].other_troopids.concat(Judge.actions[i].troopid);
							for (let troopid_tmp of troopids){
								sortedTroopid.findAndRemove(troopid_tmp);
							}
							for (let troopid_tmp of troopids){
								if (Campaign.troop_plane[troopid_tmp] < 0){
									continue;
								}
								sortedTroopid.insert(troopid_tmp);
							}
						}
						break;
					}
				}

			} while (Judge.actions.length > oldActionLength); // 直到找不到新的可以干的事了

			// 最后再更新一次本回合完全处于修整的部队的状态 (更新状态也是 action)


			// 作战判定 demo
			// Judge.judgeDemo();

			// 作战判定完毕，恢复原始数据
			Campaign.setData(Judge.dataSaved);
			Campaign.cacheCommon();

			return Judge.actions;
		}

		/** 发放所有部队的移动力 */
		private static troopMoveRemainAll() : void {
			let move_remains = [];
			for (let troop of Campaign.troop) {
				if (troop.plane < 0) {
					move_remains.push(troop.move_remain);
					continue;
				}
				if (troop.move_remain < 0) {
					move_remains.push(troop.move_remain + troop.move);
				} else {
					move_remains.push(troop.move);
				}
			}
			let moveRemainAction = new MoveRemainAction()
			Judge.actions.push(ActionUtil.setAction(moveRemainAction, -1, -1, 9999, [], move_remains));
			moveRemainAction.acceptOnce();
		}

		/**
		 * 缓存所有部队的 strategy
		 */
		private static cacheStrategiesAll() : void {
			Judge.strategiesAll = Util.createArray(Campaign.troop.length, -1);
			for (let direction of Interaction.directions) {
				for (let i = 0; i < direction.strategies.length; i++) {
					if (direction.strategies[i] >= 0) {
						Judge.strategiesAll[i] = direction.strategies[i];
					}
				}
			}
		}

		/**
		 * 缓存所有部队的初始血量，用于击溃判定
		 */
		private static cacheStrengthsAll() : void {
			Judge.strengthsAll = [];
			for (let troop of Campaign.troop) {
				Judge.strengthsAll.push(troop.strength);
			}
		}

		/**
		 * 缓存所有部队的目标点位
		 */
		private static cacheTargetTilesAll() : void {
			Judge.targetTilesAll = [];
			for (let troop of Campaign.troop){
				Judge.targetTilesAll.push(new Tile(troop.tile.x, troop.tile.y));
			}
			for (let direction of Interaction.directions) {
				for (let team of direction.teams) {
					for (let troopid of team.troopids) {
						let troop = Campaign.troop[troopid];
						if (team.tid < 0) {
							Judge.targetTilesAll[troopid] = new Tile(troop.tile.x, troop.tile.y);
						} else {
							Judge.targetTilesAll[troopid] = Map.tileEngine.tid2tile(team.tid);
						}
					}
				}
			}
		}

		/**
		 * 哪个部队先行动
		 */
		public static actionPrior(troopid : number) : number {
			let troop = Campaign.troop[troopid];
			let priority = (troop.move_remain / troop.move) + 0.00001 * troop.attack_range;
			if (troop.type_name == "航空兵") {
				priority -= 0.00005;
			}
			return priority;
		}

		// 寻找攻击范围内威胁最大敌人
		private static findNearEnemy(troop : TroopEx, centertid : number = -1, range : number = -1, includingCenter : boolean = false, threatenSelf : boolean = false) : number {
			if (centertid < 0) {
				centertid = troop.tid;
			}
			if (range < 0) {
				range = troop.attack_range;
			}
			let tids : number[];
			if (range == 1) {
				tids = Map.tileEngine.neighborCache[centertid];
			} else {
				tids = [];
				let payMap = Map.routeEngine.payMap(Util.createArray(Map.tileEngine.tileCount, 1), centertid, 2 * range).payMap;
				for (let i = 0; i < payMap.length; i++) {
					if (payMap[i] >= 0 && payMap[i] <= 2 * range) {
						tids.push(i);
					}
				}
			}
			if (includingCenter) {
				tids.push(centertid);
			}

			let maxPriority = 0;
			let maxPriorityTroopid = -1;
			for (let tid of tids) {
				let troopid_tmp = Campaign.troop_map[tid];
				if (troopid_tmp != -1){
					let troop_tmp = Campaign.troop[troopid_tmp];
					if (troop_tmp.force != troop.force) {
						let priority = troop_tmp.priority;
						if (threatenSelf) {
							priority += troop_tmp.attack_efficiency[troop.troop_type] / 0.05; // 常数 0.05 是通用攻击效率
						}
						if (priority > maxPriority) {
							maxPriority = priority;
							maxPriorityTroopid = troopid_tmp;
						}
					}
				}
			}
			return maxPriorityTroopid;
		}

		// 寻找合适的路径
		private static findMarchRoute(troop : TroopEx, troopid : number) : RoutePay {
			if (Util.sameXY(troop.tile, Judge.targetTilesAll[troopid])) {
				return null;
			}

			// 本单位的移动地图
			// TODO 地图使用本势力地图
			let moveMap = Campaign.zone.pass_index.concat();
			for (let tid = 0; tid < moveMap.length; tid++) {
				if (! troop.move_ability[Campaign.zone.land_type[tid]]){
					moveMap[tid] = -1;
				}
			}
			for (let troopid_tmp of Campaign.force[troop.force].innerTroop){
				let troop_tmp = Campaign.troop[troopid_tmp];
				if (troop_tmp.tid != troop.tid && troop_tmp.force == troop.force){ // 我方的单位
					moveMap[troop_tmp.tid] = -1;
				}
			}

			let target_tid = Map.tileEngine.tile2tid(Judge.targetTilesAll[troopid]);

			let endPayMap = Judge.payMapCache[target_tid];
			let startPayMap = Map.routeEngine.payMap(moveMap, troop.tid, 50000).payMap;

			let minPay = 50000;
			let minPayTid = -1;
			for (let tid = 0; tid < Map.tileEngine.tileCount; tid++) {
				if (startPayMap[tid] >= 0 && endPayMap[tid] >= 0){
					let payTmp = startPayMap[tid] + 5 * endPayMap[tid]; // 与目标点距离优先，兼顾移动过去的耗费
					if (payTmp < minPay) {
						minPay = payTmp;
						minPayTid = tid;
					}
				}
			}

			let target_tile = Map.tileEngine.tid2tile(minPayTid);
			if (Util.sameXY(troop.tile, target_tile)) {
				return null;
			}

			return Map.routeEngine.routePay(moveMap, troop.tid, minPayTid, true);
		}

		// 寻找撤退路径，如果没找到则找最近的撤退点，完全被包围则返回 null
		private static findRetreatRoute(troop : TroopEx, troopid : number) : RoutePay {
			// 本单位的移动地图
			// TODO 地图使用本势力地图
			let moveMapEscape = Campaign.zone.pass_index.concat();
			for (let tid = 0; tid < moveMapEscape.length; tid++) {
				if (! troop.move_ability[Campaign.zone.land_type[tid]]){
					moveMapEscape[tid] = -1;
				}
			}
			for (let troop_tmp of Campaign.troop){
				if (troop_tmp.plane == 0){
					moveMapEscape[troop_tmp.tid] = -1;
				}
			}
			moveMapEscape[troop.tid] = Campaign.zone.pass_index[troop.tid];

			// 撤退时不能侧面从敌人旁边过，逃跑时哪里能跑哪里跑
			let moveMap = moveMapEscape.concat(); // 先把 逃跑moveMap 拷出来，再修改 moveMap

			for (let troop_tmp of Campaign.troop){
				if (troop_tmp.plane == 0 && troop_tmp.force != troop.force){
					for (let neighbortid of Map.tileEngine.neighborCache[troop_tmp.tid]){
						moveMap[neighbortid] = -1;
					}
				}
			}
			moveMap[troop.tid] = Campaign.zone.pass_index[troop.tid];

			let routePay = Map.routeEngine.routePay(moveMap, troop.tid, Map.tileEngine.tile2tid(troop.force_tile), true);
			if (routePay.pay >= 0) {
				return routePay;
			}

			let payMap = Map.routeEngine.payMap(moveMapEscape, troop.tid, 50000).payMap;
			let minPay = 50000;
			let minPayTid = -1;
			for (let tid = 0; tid < Map.tileEngine.tileCount; tid++) {
				if (payMap[tid] >= 0 && payMap[tid] < 32767){
					let payTmp = payMap[tid]; // 与目标点距离优先，兼顾移动过去的耗费
					if (payTmp < minPay) {
						minPay = payTmp;
						minPayTid = tid;
					}
				}
			}

			let target_tile = Map.tileEngine.tid2tile(minPayTid);
			if (Util.sameXY(troop.tile, target_tile)) {
				return null;
			}

			return Map.routeEngine.routePay(moveMap, troop.tid, minPayTid, true);
		}

		// 目标策略
		private static targetStrategy(troop : TroopEx, troopid : number) : void {
			// 计算移动路径
			let routePay = this.findMarchRoute(troop, troopid);
			if (routePay == null) {
				return;
			}

			let nextTid = routePay.route[routePay.route.length - 2];

			let tgtTroopid = Campaign.troop_map[nextTid];
			if (tgtTroopid >= 0 && Campaign.troop[tgtTroopid].force != troop.force) {
				// 如果移动路径上有能攻击到的敌人，则攻击最近邻的一个
				let tgtTroop = Campaign.troop[tgtTroopid];
				Judge.tryAttack(troop, troopid, tgtTroop, tgtTroopid);

				let loss_rate = Judge.tryRetreat(tgtTroop, tgtTroopid);
				if (loss_rate > 0) {
					// 若敌军撤退，可执行移动，抢占先机
					Judge.tryMove(troop, troopid, routePay);
				}
				Judge.tryDie(tgtTroop, tgtTroopid);
				Judge.tryDie(troop, troopid);

			} else {
				// 如果没有，则移动
				Judge.tryMove(troop, troopid, routePay);
			}
		}

		// 战线策略
		private static lineStrategy(troop : TroopEx, troopid : number) : void {
			// 寻找攻击范围内有没有敌人
			let tgtTroopid = this.findNearEnemy(troop);
			if (tgtTroopid >= 0) { // 如果有敌人，攻击最具威胁的敌人
				let tgtTroop = Campaign.troop[tgtTroopid];
				Judge.tryAttack(troop, troopid, tgtTroop, tgtTroopid);

				let loss_rate = Judge.tryRetreat(tgtTroop, tgtTroopid);
				if (loss_rate > 0) {
					// 若敌军撤退，可执行移动，抢占先机
					let tgtTroopid = this.findNearEnemy(troop);
					if (tgtTroopid < 0) {
						let routePay = this.findMarchRoute(troop, troopid);
						Judge.tryMove(troop, troopid, routePay);
					}
				}
				Judge.tryDie(tgtTroop, tgtTroopid);
				Judge.tryDie(troop, troopid);

			} else { // 如果没有，且未移动完毕，则计算移动路径并移动
				let routePay = this.findMarchRoute(troop, troopid);
				Judge.tryMove(troop, troopid, routePay);
			}
		}

		// 磁性策略
		private static magnetStrategy(troop : TroopEx, troopid : number) : void {
			// 寻找攻击范围内有没有敌人
			let tgtTroopid = this.findNearEnemy(troop, -1, 1);
			if (tgtTroopid >= 0) {
				// 如果有敌人，则停驻
				return;
			}

			// 如果没有，且未移动完毕，则计算移动路径并移动
			let routePay = this.findMarchRoute(troop, troopid);
			Judge.tryMove(troop, troopid, routePay);
		}

		private static bombStrategy(troop : TroopEx, troopid : number) : void {
			if (Util.sameXY(Judge.targetTilesAll[troopid], troop.tile)) {
				return;
			}
			let tid = Map.tileEngine.tile2tid(Judge.targetTilesAll[troopid]);
			let tgtTroopid = this.findNearEnemy(troop, tid, 1, true, true);
			if (tgtTroopid >= 0) { // 如果有敌人，攻击最具威胁的敌人
				let tgtTroop = Campaign.troop[tgtTroopid];
				Judge.tryBombAttack(troop, troopid, tgtTroop, tgtTroopid, tid);

				Judge.tryRetreat(tgtTroop, tgtTroopid);
				Judge.tryDie(tgtTroop, tgtTroopid);
				Judge.tryDie(troop, troopid);
			} else {
				let action = new ScoutAction();
				Judge.actions.push(ActionUtil.setAction(action, troopid, 0, tid));
				action.acceptOnce();
			}
		}

		private static tryRegroup(troopid : number, tid : number) : void {
			if (tid >= 0 && tid < 1000000){
				let troop = Campaign.troop[troopid];
				let tgtTroopid = Campaign.troop_map[tid];
				let pay = Map.routeEngine.routePay(Campaign.zone.pass_index, troop.tid, tid, false).pay;
				if (tgtTroopid < 0) {
					// 新建一个 troop 移动到目标地点
					let move_remain_self = troop.move_remain - 0.5 * troop.move;
					let move_remain_new = move_remain_self - pay;
					let action = new RegroupAction();
					Judge.actions.push(ActionUtil.setAction(action, troopid, move_remain_self, tid, [], [move_remain_new]));
					action.acceptOnce();

				} else {
					// 合并到另一支部队并删除之
					let tgtTroop = Campaign.troop[tgtTroopid];
					let strengthSum = troop.strength + tgtTroop.strength;
					let rateSrc = troop.strength / strengthSum;
					let rateTgt = tgtTroop.strength / strengthSum;

					let move_remain_self = troop.move_remain - pay;
					let move_remain_tgt = (rateSrc * move_remain_self + rateTgt * tgtTroop.move_remain) - 0.5 * tgtTroop.move;

					let action = new RegroupAction();
					Judge.actions.push(ActionUtil.setAction(action, troopid, -100, tid, [], [move_remain_tgt]));
					action.acceptOnce();
				}
			}
		}

		// 进军移动
		private static tryMove(troop : TroopEx, troopid : number, routePay : RoutePay) : void {
			if (routePay == null) {
				return;
			}
			let nextTid = routePay.route[routePay.route.length - 2];
			let pay = Campaign.zone.pass_index[troop.tid] + Campaign.zone.pass_index[nextTid];
			let move_remain = troop.move_remain - pay;

			// 更新 data
			let action = new MoveAction();
			Judge.actions.push(ActionUtil.setAction(action, troopid, move_remain, nextTid));
			action.acceptOnce();
		}

		// 攻击敌军
		private static tryAttack(troop : TroopEx, troopid : number, tgtTroop : TroopEx, tgtTroopid : number) : void {
			let damage = JudgeMath.attackDamage(troop, tgtTroop);
			let action = new AttackAction(troop.strength - damage.damageAttack, troop.morale, tgtTroop.strength - damage.damageDefend, tgtTroop.morale);
			Judge.actions.push(ActionUtil.setAction(action, troopid, 0, troop.tid, [tgtTroopid], [0]));
			action.acceptOnce();
		}

		// 轰炸敌军
		private static tryBombAttack(troop : TroopEx, troopid : number, tgtTroop : TroopEx, tgtTroopid : number, tid : number) : void {
			let damage = JudgeMath.attackDamage(troop, tgtTroop);
			let action = new BombAction(troop.strength - damage.damageAttack, troop.morale, tgtTroop.strength - damage.damageDefend, tgtTroop.morale);
			Judge.actions.push(ActionUtil.setAction(action, troopid, 0, tid, [tgtTroopid], [0]));
			action.acceptOnce();
		}

		// 看看该部队有没有消亡，消亡则添加消亡action
		private static tryDie(troop : TroopEx, troopid : number) : void {
			if (troop.strength <= 0) {
				let action = new DieAction();
				Judge.actions.push(ActionUtil.setAction(action, troopid, -100));
				action.acceptOnce();
			}
		}

		// 是否撤退/逃跑，计算逃跑路线，如果撤退，则返回损失比例；如果损失比例为损失比例的负数，则表示甚至无处逃跑
		private static tryRetreat(troop : TroopEx, troopid : number) : number {
			if (Campaign.troop_plane[troopid] == 0) {
				if (Judge.strategiesAll[troopid] == 0) {
					return 0;
				}
				let oldStrength = Judge.strengthsAll[troopid];
				let lossRate = (oldStrength - troop.strength) / oldStrength;
				if (Judge.strategiesAll[troopid] == 1) {
					if ( lossRate < Judge.judgeParam.retreat_damage ) {
						return 0;
					}
				}
				let routePay = Judge.findRetreatRoute(troop, troopid);
				if (routePay == null) {
					return -lossRate;
				}
				let nextTid = routePay.route[routePay.route.length - 2];
				let pay = Campaign.zone.pass_index[troop.tid] + Campaign.zone.pass_index[nextTid];
				let move_loss = lossRate < Judge.judgeParam.retreat_damage ? pay : Judge.judgeParam.retreat_move_factor * Math.max(troop.move, pay);
				let move_remain = troop.move_remain - move_loss;

				// 更新 data
				let action = new MoveAction();
				Judge.actions.push(ActionUtil.setAction(action, troopid, move_remain, nextTid));
				action.acceptOnce();

				return lossRate;
			}
		}

		// 示例，代码功能原型
		// private static judgeDemo() : void {
		// 	for (let force_direction of Interaction.directions) {
		// 		for (let team of force_direction.teams) {
		// 			if (team.tid < 0) {
		// 				continue;
		// 			}
		// 			for (let troopid of team.troopids) {
		// 				let troop = Campaign.troop[troopid];
		// 				let r = Map.routeEngine.routePay(Campaign.zone.pass_index, troop.tid, team.tid, true);
		// 				if (r.pay < 0) {
		// 					continue;
		// 				}
		// 				Judge.actions.push(ActionUtil.setAction(new MoveAction(), troopid, 0, r.route[r.route.length - 2]));
		// 			}
		// 		}
		// 	}
		// }
	}
}